﻿//using OpenTK;
//using System;
//using System.Collections;
//using System.Collections.Generic;
//using System.Windows.Forms;


//namespace LightCAD.Three
//{

//    public class TransformControls : Object3D
//    {
//        private struct Unit
//        {
//            public Vector3 X;
//            public Vector3 Y;
//            public Vector3 Z;

//            public new Vector3 this[string axis]
//            {
//                get
//                {
//                    switch (axis)
//                    {
//                        case "X":
//                            return this.X;
//                        case "Y":
//                            return this.Y;
//                        case "Z":
//                            return this.Z;
//                    }
//                    return null;
//                }
//                set
//                {
//                    switch (axis)
//                    {
//                        case "X":
//                            this.X = value;
//                            break;
//                        case "Y":
//                            this.Y = value;
//                            break;
//                        case "Z":
//                            this.Z = value;
//                            break;
//                    }
//                }
//            }
//        }

//        private struct Pointer
//        {
//            public double x;
//            public double y;
//            public MouseButtons button;
//            public Vector2 ToV2()
//            {
//                return new Vector2(x, y);
//            }
//        }

//        private static Euler _tempEuler = new Euler();
//        private static Vector3 _alignVector = new Vector3(0, 1, 0);
//        private static Vector3 _zeroVector = new Vector3(0, 0, 0);
//        private static Matrix4 _lookAtMatrix = new Matrix4();
//        private static Quaternion _tempQuaternion2 = new Quaternion();
//        private static Quaternion _identityQuaternion = new Quaternion();
//        private static Vector3 _dirVector = new Vector3();
//        private static Matrix4 _tempMatrix = new Matrix4();

//        private static Vector3 _unitX = new Vector3(1, 0, 0);
//        private static Vector3 _unitY = new Vector3(0, 1, 0);
//        private static Vector3 _unitZ = new Vector3(0, 0, 1);

//        private static Vector3 _v1 = new Vector3();
//        private static Vector3 _v2 = new Vector3();
//        private static Vector3 _v3 = new Vector3();

//        public class TransformControlsGizmo : Object3D
//        {
//            private struct ControlInfo
//            {
//                public Object3D obj3d;
//                public double[] position;
//                public double[] rotation;
//                public double[] scale;
//                public string tag;
//                public ControlInfo(Object3D obj3d, double[] position = null, double[] rotation = null, double[] scale = null, string tag = null)
//                {
//                    this.obj3d = obj3d;
//                    this.position = position;
//                    this.rotation = rotation;
//                    this.scale = scale;
//                    this.tag = tag;
//                }
//            }

//            public JsObj<string, Object3D> gizmo;
//            public JsObj<string, Object3D> picker;
//            public JsObj<string, Object3D> helper;
//            public string mode;
//            public string space;
//            public Quaternion worldQuaternion;
//            public Quaternion worldQuaternionStart;
//            public Vector3 worldPosition;
//            public Vector3 worldPositionStart;
//            public Camera camera;
//            public Vector3 cameraPosition;
//            public double size;
//            public string axis;
//            public Vector3 eye;
//            public Vector3 rotationAxis;
//            public bool dragging;

//            public bool showX;
//            public bool showY;
//            public bool showZ;

//            public bool enabled;
//            //public Object3D obj;
//            //public object translationSnap;
//            //public object rotationSnap;
//            //public object scaleSnap;

//            public TransformControlsGizmo() : base()
//            {
//                this.type = "TransformControlsGizmo";

//                // shared materials

//                var gizmoMaterial = new MeshBasicMaterial
//                {
//                    depthTest = false,
//                    depthWrite = false,
//                    fog = false,
//                    toneMapped = false,
//                    transparent = true
//                });

//                var gizmoLineMaterial = new LineBasicMaterial
//                {
//                    depthTest = false,
//                    depthWrite = false,
//                    fog = false,
//                    toneMapped = false,
//                    transparent = true
//                });

//                // Make unique material for each axis/color

//                var matInvisible = gizmoMaterial.clone();
//                matInvisible.opacity = 0.15;

//                var matHelper = gizmoLineMaterial.clone();
//                matHelper.opacity = 0.5;

//                var matRed = gizmoMaterial.clone();
//                matRed.color.setHex(0xff0000);

//                var matGreen = gizmoMaterial.clone();
//                matGreen.color.setHex(0x00ff00);

//                var matBlue = gizmoMaterial.clone();
//                matBlue.color.setHex(0x0000ff);

//                var matRedTransparent = gizmoMaterial.clone();
//                matRedTransparent.color.setHex(0xff0000);
//                matRedTransparent.opacity = 0.5;

//                var matGreenTransparent = gizmoMaterial.clone();
//                matGreenTransparent.color.setHex(0x00ff00);
//                matGreenTransparent.opacity = 0.5;

//                var matBlueTransparent = gizmoMaterial.clone();
//                matBlueTransparent.color.setHex(0x0000ff);
//                matBlueTransparent.opacity = 0.5;

//                var matWhiteTransparent = gizmoMaterial.clone();
//                matWhiteTransparent.opacity = 0.25;

//                var matYellowTransparent = gizmoMaterial.clone();
//                matYellowTransparent.color.setHex(0xffff00);
//                matYellowTransparent.opacity = 0.25;

//                var matYellow = gizmoMaterial.clone();
//                matYellow.color.setHex(0xffff00);

//                var matGray = gizmoMaterial.clone();
//                matGray.color.setHex(0x787878);

//                // reusable geometry

//                var arrowGeometry = new CylinderGeometry(0, 0.04, 0.1, 12);
//                arrowGeometry.translate(0, 0.05, 0);

//                var scaleHandleGeometry = new BoxGeometry(0.08, 0.08, 0.08);
//                scaleHandleGeometry.translate(0, 0.04, 0);

//                var lineGeometry = new BufferGeometry();
//                lineGeometry.setAttribute("position", new Float32BufferAttribute(new double[] { 0, 0, 0, 1, 0, 0 }, 3));

//                var lineGeometry2 = new CylinderGeometry(0.0075, 0.0075, 0.5, 3);
//                lineGeometry2.translate(0, 0.25, 0);

//                TorusGeometry CircleGeometry(double radius, double arc)
//                {
//                    var geometry = new TorusGeometry(radius, 0.0075, 3, 64, arc * Math.PI * 2);
//                    geometry.rotateY(Math.PI / 2);
//                    geometry.rotateX(Math.PI / 2);
//                    return geometry;
//                }

//                // Special geometry for transform helper. If scaled with position vector it spans from [0,0,0] to position
//                BufferGeometry TranslateHelperGeometry()
//                {
//                    var geometry = new BufferGeometry();
//                    geometry.setAttribute("position", new Float32BufferAttribute(new double[] { 0, 0, 0, 1, 1, 1 }, 3));
//                    return geometry;
//                }

//                // Gizmo definitions - custom hierarchy definitions for setupGizmo() function

//                var gizmoTranslate = new JsObj<string, JsArr<ControlInfo>>
//                    {
//                        { "X", new JsArr<ControlInfo>
//                            {
//                                new ControlInfo(new Mesh(arrowGeometry, matRed ), new double[]{ 0.5, 0, 0 }, new double[]{ 0, 0, - Math.PI / 2 }),
//                                new ControlInfo(new Mesh(arrowGeometry, matRed ), new double[]{ -0.5, 0, 0  }, new double[]{ 0, 0, Math.PI / 2 }),
//                                new ControlInfo(new Mesh(lineGeometry2, matRed ), new double[]{ 0, 0, 0   }, new double[]{ 0, 0, - Math.PI / 2 }),
//                            }
//                        },
//                        { "Y", new JsArr<ControlInfo>
//                            {
//                                new ControlInfo(new Mesh(arrowGeometry, matGreen), new double[]{ 0, 0.5, 0 }, null),
//                                new ControlInfo(new Mesh(arrowGeometry, matGreen), new double[]{ 0, -0.5, 0 }, new double[]{Math.PI, 0, 0 }),
//                                new ControlInfo(new Mesh(lineGeometry2, matGreen), null, null)
//                            }
//                        },
//                        {"Z", new JsArr<ControlInfo>
//                            {
//                                new ControlInfo(new Mesh(arrowGeometry, matBlue), new double[]{ 0, 0, 0.5 }, new double[]{ Math.PI / 2, 0, 0 } ),
//                                new ControlInfo(new Mesh(arrowGeometry, matBlue), new double[]{ 0, 0, -0.5 }, new double[]{ -Math.PI / 2, 0, 0 } ),
//                                new ControlInfo(new Mesh(lineGeometry2, matBlue), null, new double[]{ Math.PI / 2, 0, 0 } )
//                            }
//                        },
//                        { "XYZ", new JsArr<ControlInfo>
//                            {
//                                new ControlInfo( new Mesh(new OctahedronGeometry(0.1, 0), matWhiteTransparent.clone()), new double[]{ 0, 0, 0 }, null )
//                            }
//                        },
//                        { "XY", new JsArr<ControlInfo>
//                            {
//                                new ControlInfo(new Mesh(new BoxGeometry(0.15, 0.15, 0.01), matBlueTransparent.clone()), new double[]{ 0.15, 0.15, 0 }, null )
//                            }
//                        },
//                        { "YZ", new JsArr<ControlInfo>
//                            {
//                                new ControlInfo(new Mesh(new BoxGeometry(0.15, 0.15, 0.01), matRedTransparent.clone()), new double[]{ 0, 0.15, 0.15 }, new double[]{ 0, Math.PI / 2, 0 } )
//                            }
//                        },
//                        { "XZ",new JsArr<ControlInfo>
//                            {
//                                new ControlInfo( new Mesh(new BoxGeometry(0.15, 0.15, 0.01), matGreenTransparent.clone()), new double[]{ 0.15, 0, 0.15 }, new double[]{ -Math.PI / 2, 0, 0 } )
//                            }
//                        }
//                    };

//                var pickerTranslate = new JsObj<string, JsArr<ControlInfo>>
//                    {
//                        { "X", new JsArr<ControlInfo>
//                            {
//                                new ControlInfo( new Mesh( new CylinderGeometry( 0.2, 0, 0.6, 4 ), matInvisible ), new double[]{ 0.3, 0, 0 }, new double[]{ 0, 0, - Math.PI / 2 }),
//                                new ControlInfo( new Mesh( new CylinderGeometry( 0.2, 0, 0.6, 4 ), matInvisible ), new double[]{ - 0.3, 0, 0 }, new double[]{ 0, 0, Math.PI / 2 })
//                            }
//                        },
//                        { "Y", new JsArr<ControlInfo>
//                            {
//                                new ControlInfo( new Mesh( new CylinderGeometry( 0.2, 0, 0.6, 4 ), matInvisible ), new double[]{ 0, 0.3, 0 }, null),
//                                new ControlInfo( new Mesh( new CylinderGeometry( 0.2, 0, 0.6, 4 ), matInvisible ), new double[]{ 0, - 0.3, 0 }, new double[]{ 0, 0, Math.PI })
//                            }
//                        },
//                        { "Z", new JsArr<ControlInfo>
//                            {
//                                new ControlInfo( new Mesh( new CylinderGeometry( 0.2, 0, 0.6, 4 ), matInvisible ), new double[]{ 0, 0, 0.3 }, new double[]{Math.PI / 2, 0, 0 }),
//                                new ControlInfo( new Mesh( new CylinderGeometry( 0.2, 0, 0.6, 4 ), matInvisible ), new double[]{ 0, 0, - 0.3 }, new double[]{ - Math.PI / 2, 0, 0 })
//                            }
//                        },
//                        { "XYZ", new JsArr<ControlInfo>
//                            {
//                                new ControlInfo( new Mesh( new OctahedronGeometry( 0.2, 0 ), matInvisible ), null, null )
//                            }
//                        },
//                        { "XY", new JsArr<ControlInfo>
//                            {
//                                new ControlInfo( new Mesh( new BoxGeometry( 0.2, 0.2, 0.01 ), matInvisible ), new double[]{ 0.15, 0.15, 0 }, null)
//                            }
//                        },
//                        { "YZ", new JsArr<ControlInfo>
//                            {
//                                new ControlInfo( new Mesh( new BoxGeometry( 0.2, 0.2, 0.01 ), matInvisible ), new double[]{ 0, 0.15, 0.15 }, new double[]{ 0, Math.PI / 2, 0 })
//                            }
//                        },
//                        { "XZ", new JsArr<ControlInfo>
//                            {
//                                new ControlInfo( new Mesh( new BoxGeometry( 0.2, 0.2, 0.01 ), matInvisible ), new double[]{ 0.15, 0, 0.15 }, new double[]{ - Math.PI / 2, 0, 0 })
//                            }
//                        }
//                    };

//                var helperTranslate = new JsObj<string, JsArr<ControlInfo>>
//                    {
//                        { "START", new JsArr<ControlInfo>
//                            {
//                                new ControlInfo( new Mesh(new OctahedronGeometry(0.01, 2), matHelper), null, null, null, "helper" )
//                            }
//                        },
//                        { "END", new JsArr<ControlInfo>
//                            {
//                                new ControlInfo( new Mesh(new OctahedronGeometry(0.01, 2), matHelper), null, null, null, "helper" )
//                            }
//                        },
//                        { "DELTA", new JsArr<ControlInfo>
//                            {
//                                new ControlInfo(new Line(TranslateHelperGeometry(), matHelper), null, null, null, "helper" )
//                            }
//                        },
//                        { "X", new JsArr<ControlInfo>
//                            {
//                                new ControlInfo( new Line(lineGeometry, matHelper.clone()), new double[]{ -1e3, 0, 0 }, null, new double[]{ 1e6, 1, 1 }, "helper" )
//                            }
//                        },
//                        { "Y", new JsArr<ControlInfo>
//                            {
//                                new ControlInfo( new Line(lineGeometry, matHelper.clone()), new double[]{ 0, -1e3, 0 }, new double[]{ 0, 0, Math.PI / 2 }, new double[] { 1e6, 1, 1 }, "helper" )
//                            }
//                        },
//                        { "Z", new JsArr<ControlInfo>
//                            {
//                                new ControlInfo( new Line(lineGeometry, matHelper.clone()), new double[]{ 0, 0, -1e3 }, new double[]{ 0, -Math.PI / 2, 0 }, new double[]{ 1e6, 1, 1 }, "helper" )
//                            }
//                        }
//                    };

//                var gizmoRotate = new JsObj<string, JsArr<ControlInfo>>
//                    {
//                        { "XYZE", new JsArr<ControlInfo>
//                            {
//                                new ControlInfo( new Mesh(CircleGeometry(0.5, 1), matGray), null, new double[] { 0, Math.PI / 2, 0 })
//                            }
//                        },
//                        { "X", new JsArr<ControlInfo>
//                            {
//                                new ControlInfo( new Mesh(CircleGeometry(0.5, 0.5), matRed) )
//                            }
//                        },
//                        { "Y", new JsArr<ControlInfo>
//                            {
//                                new ControlInfo( new Mesh(CircleGeometry(0.5, 0.5), matGreen), null, new double[] { 0, 0, - Math.PI / 2 })
//                            }
//                        },
//                        { "Z", new JsArr<ControlInfo>
//                            {
//                                new ControlInfo( new Mesh(CircleGeometry(0.5, 0.5), matBlue), null, new double[] { 0, Math.PI / 2, 0 })
//                            }
//                        },
//                        { "E", new JsArr<ControlInfo>
//                            {
//                                new ControlInfo( new Mesh(CircleGeometry(0.75, 1), matYellowTransparent), null, new double[] { 0, Math.PI / 2, 0 })
//                            }
//                        }
//                    };

//                var helperRotate = new JsObj<string, JsArr<ControlInfo>>
//                    {
//                        { "AXIS", new JsArr<ControlInfo>
//                            {
//                                new ControlInfo( new Line(lineGeometry, matHelper.clone()), new double[]{ - 1e3, 0, 0 }, null, new double[] { 1e6, 1, 1 }, "helper" )
//                            }
//                        }
//                    };

//                var pickerRotate = new JsObj<string, JsArr<ControlInfo>>
//                    {
//                        { "XYZE", new JsArr<ControlInfo>
//                            {
//                                new ControlInfo(new Mesh(new SphereGeometry(0.25, 10, 8), matInvisible) )
//                            }
//                        },
//                        { "X", new JsArr<ControlInfo>
//                            {
//                                new ControlInfo( new Mesh(new TorusGeometry(0.5, 0.1, 4, 24), matInvisible), new double[]{ 0, 0, 0 }, new double[]{ 0, - Math.PI / 2, - Math.PI / 2 })
//                            }
//                        },
//                        { "Y", new JsArr<ControlInfo>
//                            {
//                                new ControlInfo( new Mesh(new TorusGeometry(0.5, 0.1, 4, 24), matInvisible), new double[]{ 0, 0, 0 }, new double[]{Math.PI / 2, 0, 0 })
//                            }
//                        },
//                        { "Z", new JsArr<ControlInfo>
//                            {
//                                new ControlInfo(  new Mesh(new TorusGeometry(0.5, 0.1, 4, 24), matInvisible), new double[] { 0, 0, 0 }, new double[] { 0, 0, - Math.PI / 2 }),
//                            }
//                        },
//                        { "E", new JsArr<ControlInfo>
//                            {
//                                new ControlInfo(  new Mesh(new TorusGeometry(0.75, 0.1, 2, 24), matInvisible) )
//                            }
//                        }
//                    };

//                var gizmoScale = new JsObj<string, JsArr<ControlInfo>>
//                    {
//                        { "X", new JsArr<ControlInfo>
//                            {
//                                new ControlInfo( new Mesh(scaleHandleGeometry, matRed), new double[]{ 0.5, 0, 0 }, new double[]{ 0, 0, - Math.PI / 2 }),
//                                new ControlInfo( new Mesh(lineGeometry2, matRed), new double[]{ 0, 0, 0 }, new double[]{ 0, 0, - Math.PI / 2 }),
//                                new ControlInfo( new Mesh(scaleHandleGeometry, matRed), new double[]{ - 0.5, 0, 0 }, new double[]{ 0, 0, Math.PI / 2 }),
//                            }
//                        },
//                        { "Y", new JsArr<ControlInfo>
//                            {
//                                new ControlInfo( new Mesh(scaleHandleGeometry, matGreen), new double[]{ 0, 0.5, 0 }),
//                                new ControlInfo( new Mesh(lineGeometry2, matGreen) ),
//                                new ControlInfo( new Mesh(scaleHandleGeometry, matGreen), new double[]{ 0, - 0.5, 0 }, new double[]{ 0, 0, Math.PI}),
//                            }
//                        },
//                        { "Z", new JsArr<ControlInfo>
//                            {
//                                new ControlInfo( new Mesh(scaleHandleGeometry, matBlue), new double[]{ 0, 0, 0.5 }, new double[]{Math.PI / 2, 0, 0 }),
//                                new ControlInfo( new Mesh(lineGeometry2, matBlue), new double[]{ 0, 0, 0 }, new double[]{Math.PI / 2, 0, 0 }),
//                                new ControlInfo( new Mesh(scaleHandleGeometry, matBlue), new double[]{ 0, 0, - 0.5 }, new double[]{ - Math.PI / 2, 0, 0 })
//                            }
//                        },
//                        { "XY", new JsArr<ControlInfo>
//                            {
//                                new ControlInfo( new Mesh(new BoxGeometry(0.15, 0.15, 0.01), matBlueTransparent), new double[]{ 0.15, 0.15, 0 })
//                            }
//                        },
//                        { "YZ", new JsArr<ControlInfo>
//                            {
//                                new ControlInfo( new Mesh(new BoxGeometry(0.15, 0.15, 0.01), matRedTransparent), new double[]{ 0, 0.15, 0.15 }, new double[]{ 0, Math.PI / 2, 0 })
//                            }
//                        },
//                        { "XZ", new JsArr<ControlInfo>
//                            {
//                                new ControlInfo( new Mesh(new BoxGeometry(0.15, 0.15, 0.01), matGreenTransparent), new double[]{ 0.15, 0, 0.15 }, new double[]{ - Math.PI / 2, 0, 0 })
//                            }
//                        },
//                        { "XYZ", new JsArr<ControlInfo>
//                            {
//                                new ControlInfo( new Mesh(new BoxGeometry(0.1, 0.1, 0.1), matWhiteTransparent.clone()) ),
//                            }
//                        }
//                    };

//                var pickerScale = new JsObj<string, JsArr<ControlInfo>>
//                    {
//                        {"X", new JsArr<ControlInfo>
//                            {
//                                new ControlInfo( new Mesh(new CylinderGeometry(0.2, 0, 0.6, 4), matInvisible), new double[]{ 0.3, 0, 0 }, new double[]{ 0, 0, - Math.PI / 2 }),
//                                new ControlInfo(new Mesh(new CylinderGeometry(0.2, 0, 0.6, 4), matInvisible), new double[]{ -0.3, 0, 0 }, new double[]{ 0, 0, Math.PI / 2 })
//                            }
//                        },
//                        {"Y", new JsArr<ControlInfo>
//                            {
//                                new ControlInfo( new Mesh(new CylinderGeometry(0.2, 0, 0.6, 4), matInvisible), new double[]{ 0, 0.3, 0 }),
//                                new ControlInfo( new Mesh(new CylinderGeometry(0.2, 0, 0.6, 4), matInvisible), new double[]{ 0, - 0.3, 0 }, new double[]{ 0, 0, Math.PI})
//                            }
//                        },
//                        {"Z", new JsArr<ControlInfo>
//                            {
//                                new ControlInfo( new Mesh(new CylinderGeometry(0.2, 0, 0.6, 4), matInvisible), new double[]{ 0, 0, 0.3 }, new double[]{Math.PI / 2, 0, 0 }),
//                                new ControlInfo( new Mesh(new CylinderGeometry(0.2, 0, 0.6, 4), matInvisible), new double[]{ 0, 0, - 0.3 }, new double[]{ - Math.PI / 2, 0, 0 })
//                            }
//                        },

//                        {"XY", new JsArr<ControlInfo>
//                            {
//                                new ControlInfo( new Mesh(new BoxGeometry(0.2, 0.2, 0.01), matInvisible), new double[]{ 0.15, 0.15, 0 }),
//                            }
//                        },

//                        {"YZ", new JsArr<ControlInfo>
//                            {
//                                new ControlInfo( new Mesh(new BoxGeometry(0.2, 0.2, 0.01), matInvisible), new double[]{ 0, 0.15, 0.15 }, new double[]{ 0, Math.PI / 2, 0 }),
//                            }
//                        },

//                        {"XZ", new JsArr<ControlInfo>
//                            {
//                                new ControlInfo( new Mesh(new BoxGeometry(0.2, 0.2, 0.01), matInvisible), new double[]{ 0.15, 0, 0.15 }, new double[]{ - Math.PI / 2, 0, 0 }),
//                            }
//                        },

//                        {"XYZ", new JsArr<ControlInfo>
//                            {
//                                new ControlInfo( new Mesh(new BoxGeometry(0.2, 0.2, 0.2), matInvisible), new double[]{ 0, 0, 0 }),
//                            }
//                        }
//                    };

//                var helperScale = new JsObj<string, JsArr<ControlInfo>>
//                    {
//                        { "X", new JsArr<ControlInfo>
//                            {
//                                new ControlInfo( new Line(lineGeometry, matHelper.clone()), new double[] { - 1e3, 0, 0 }, null, new double[] { 1e6, 1, 1 }, "helper" )
//                            }
//                        },
//                        { "Y", new JsArr<ControlInfo>
//                            {
//                                new ControlInfo( new Line(lineGeometry, matHelper.clone()), new double[] { 0, - 1e3, 0 }, new double[] { 0, 0, Math.PI / 2 }, new double[] { 1e6, 1, 1 }, "helper" )
//                            }
//                        },
//                        { "Z", new JsArr<ControlInfo>
//                            {
//                                new ControlInfo( new Line(lineGeometry, matHelper.clone()), new double[] { 0, 0, - 1e3 }, new double[] { 0, - Math.PI / 2, 0 }, new double[] { 1e6, 1, 1 }, "helper" )
//                            }
//                        }
//                    };

//                // Gizmo creation
//                this.gizmo = new JsObj<string, Object3D>();
//                this.picker = new JsObj<string, Object3D>();
//                this.helper = new JsObj<string, Object3D>();

//                this.add(this.gizmo["translate"] = setupGizmo(gizmoTranslate));
//                this.add(this.gizmo["rotate"] = setupGizmo(gizmoRotate));
//                this.add(this.gizmo["scale"] = setupGizmo(gizmoScale));
//                this.add(this.picker["translate"] = setupGizmo(pickerTranslate));
//                this.add(this.picker["rotate"] = setupGizmo(pickerRotate));
//                this.add(this.picker["scale"] = setupGizmo(pickerScale));
//                this.add(this.helper["translate"] = setupGizmo(helperTranslate));
//                this.add(this.helper["rotate"] = setupGizmo(helperRotate));
//                this.add(this.helper["scale"] = setupGizmo(helperScale));

//                // Pickers should be hidden always

//                this.picker["translate"].visible = false;
//                this.picker["rotate"].visible = false;
//                this.picker["scale"].visible = false;

//            }

//            /// <summary>
//            /// Creates an Object3D with gizmos described in custom hierarchy definition.
//            /// </summary>
//            private Object3D setupGizmo(JsObj<string, JsArr<ControlInfo>> gizmoMap)
//            {
//                var gizmo = new Object3D();
//                foreach (var name in gizmoMap.Keys)
//                {
//                    for (int i = 0; i < gizmoMap[name].length; i++)
//                    {
//                        var obj = gizmoMap[name][i].obj3d.clone();
//                        var position = gizmoMap[name][i].position;
//                        var rotation = gizmoMap[name][i].rotation;
//                        var scale = gizmoMap[name][i].scale;
//                        var tag = gizmoMap[name][i].tag;

//                        // name and tag properties are essential for picking and updating logic.
//                        obj.name = name;
//                        obj.userData["tag"] = tag;

//                        if (position != null)
//                        {
//                            obj.position.set(position[0], position[1], position[2]);
//                        }

//                        if (rotation != null)
//                        {
//                            obj.rotation.set(rotation[0], rotation[1], rotation[2]);
//                        }

//                        if (scale != null)
//                        {
//                            obj.scale.set(scale[0], scale[1], scale[2]);
//                        }

//                        obj.updateMatrix();

//                        var tempGeometry = (obj as IGeometry).getGeometry().clone();
//                        tempGeometry.applyMatrix4(obj.matrix);
//                        (obj as IGeometry).setGeometry(tempGeometry);
//                        obj.renderOrder = int.MaxValue;

//                        obj.position.set(0, 0, 0);
//                        obj.rotation.set(0, 0, 0);
//                        obj.scale.set(1, 1, 1);

//                        gizmo.add(obj);
//                    }
//                }
//                return gizmo;
//            }


//            /// <summary>
//            /// updateMatrixWorld will update transformations and appearance of individual handles
//            /// </summary>
//            public override void updateMatrixWorld(bool force = false)
//            {
//                var space = this.mode == "scale" ? "local" : this.space; // scale always oriented to local rotation

//                var quaternion = space == "local" ? this.worldQuaternion : _identityQuaternion;

//                // Show only gizmos for current transform mode

//                this.gizmo["translate"].visible = this.mode == "translate";
//                this.gizmo["rotate"].visible = this.mode == "rotate";
//                this.gizmo["scale"].visible = this.mode == "scale";

//                this.helper["translate"].visible = this.mode == "translate";
//                this.helper["rotate"].visible = this.mode == "rotate";
//                this.helper["scale"].visible = this.mode == "scale";


//                var handles = new JsArr<Object3D>();
//                handles = handles.concat(this.picker[this.mode].children);
//                handles = handles.concat(this.gizmo[this.mode].children);
//                handles = handles.concat(this.helper[this.mode].children);

//                for (int i = 0; i < handles.length; i++)
//                {
//                    var handle = handles[i];

//                    // hide aligned to camera
//                    handle.visible = true;
//                    handle.rotation.set(0, 0, 0);
//                    handle.position.copy(this.worldPosition);

//                    double factor = 0;

//                    if (this.camera is OrthographicCamera)
//                    {
//                        var ortCamera = this.camera as OrthographicCamera;
//                        factor = (ortCamera.top - ortCamera.bottom) / this.camera.zoom;
//                    }
//                    else
//                    {
//                        var perCamera = this.camera as PerspectiveCamera;
//                        factor = this.worldPosition.distanceTo(this.cameraPosition) * Math.Min(1.9 * JMath.tan(Math.PI * perCamera.fov / 360) / perCamera.zoom, 7);
//                    }

//                    handle.scale.set(1, 1, 1).multiplyScalar(factor * this.size / 4.0);

//                    // TODO: simplify helpers and consider decoupling from gizmo

//                    if (handle.userData.TryGetValue("tag", out object val) && (string)val == "helper")
//                    {
//                        handle.visible = false;

//                        if (handle.name == "AXIS")
//                        {
//                            handle.visible = this.axis != null;

//                            if (this.axis == "X")
//                            {
//                                _tempQuaternion.setFromEuler(_tempEuler.set(0, 0, 0));
//                                handle.quaternion.copy(quaternion).multiply(_tempQuaternion);

//                                if (JMath.abs(_alignVector.copy(_unitX).applyQuaternion(quaternion).dot(this.eye)) > 0.9)
//                                {
//                                    handle.visible = false;
//                                }
//                            }

//                            if (this.axis == "Y")
//                            {
//                                _tempQuaternion.setFromEuler(_tempEuler.set(0, 0, Math.PI / 2));
//                                handle.quaternion.copy(quaternion).multiply(_tempQuaternion);

//                                if (JMath.abs(_alignVector.copy(_unitY).applyQuaternion(quaternion).dot(this.eye)) > 0.9)
//                                {
//                                    handle.visible = false;
//                                }
//                            }

//                            if (this.axis == "Z")
//                            {

//                                _tempQuaternion.setFromEuler(_tempEuler.set(0, Math.PI / 2, 0));
//                                handle.quaternion.copy(quaternion).multiply(_tempQuaternion);

//                                if (JMath.abs(_alignVector.copy(_unitZ).applyQuaternion(quaternion).dot(this.eye)) > 0.9)
//                                {
//                                    handle.visible = false;
//                                }
//                            }

//                            if (this.axis == "XYZE")
//                            {
//                                _tempQuaternion.setFromEuler(_tempEuler.set(0, Math.PI / 2, 0));
//                                _alignVector.copy(this.rotationAxis);
//                                handle.quaternion.setFromRotationMatrix(_lookAtMatrix.lookAt(_zeroVector, _alignVector, _unitY));
//                                handle.quaternion.multiply(_tempQuaternion);
//                                handle.visible = this.dragging;
//                            }

//                            if (this.axis == "E")
//                            {
//                                handle.visible = false;
//                            }
//                        }
//                        else if (handle.name == "START")
//                        {
//                            handle.position.copy(this.worldPositionStart);
//                            handle.visible = this.dragging;
//                        }
//                        else if (handle.name == "END")
//                        {
//                            handle.position.copy(this.worldPosition);
//                            handle.visible = this.dragging;
//                        }
//                        else if (handle.name == "DELTA")
//                        {
//                            handle.position.copy(this.worldPositionStart);
//                            handle.quaternion.copy(this.worldQuaternionStart);
//                            _tempVector.set(1e-10, 1e-10, 1e-10).add(this.worldPositionStart).sub(this.worldPosition).multiplyScalar(-1);
//                            _tempVector.applyQuaternion(this.worldQuaternionStart.clone().invert());
//                            handle.scale.copy(_tempVector);
//                            handle.visible = this.dragging;
//                        }
//                        else
//                        {
//                            handle.quaternion.copy(quaternion);
//                            if (this.dragging)
//                            {
//                                handle.position.copy(this.worldPositionStart);
//                            }
//                            else
//                            {
//                                handle.position.copy(this.worldPosition);
//                            }

//                            if (this.axis != null)
//                            {
//                                handle.visible = this.axis.search(handle.name) != -1;
//                            }
//                        }

//                        // If updating helper, skip rest of the loop
//                        continue;
//                    }

//                    // Align handles to current local or world rotation

//                    handle.quaternion.copy(quaternion);

//                    if (this.mode == "translate" || this.mode == "scale")
//                    {
//                        // Hide translate and scale axis facing the camera

//                        var AXIS_HIDE_THRESHOLD = 0.99;
//                        var PLANE_HIDE_THRESHOLD = 0.2;

//                        if (handle.name == "X")
//                        {
//                            if (JMath.abs(_alignVector.copy(_unitX).applyQuaternion(quaternion).dot(this.eye)) > AXIS_HIDE_THRESHOLD)
//                            {
//                                handle.scale.set(1e-10, 1e-10, 1e-10);
//                                handle.visible = false;
//                            }
//                        }

//                        if (handle.name == "Y")
//                        {
//                            if (JMath.abs(_alignVector.copy(_unitY).applyQuaternion(quaternion).dot(this.eye)) > AXIS_HIDE_THRESHOLD)
//                            {
//                                handle.scale.set(1e-10, 1e-10, 1e-10);
//                                handle.visible = false;
//                            }
//                        }

//                        if (handle.name == "Z")
//                        {
//                            if (JMath.abs(_alignVector.copy(_unitZ).applyQuaternion(quaternion).dot(this.eye)) > AXIS_HIDE_THRESHOLD)
//                            {
//                                handle.scale.set(1e-10, 1e-10, 1e-10);
//                                handle.visible = false;
//                            }
//                        }

//                        if (handle.name == "XY")
//                        {
//                            if (JMath.abs(_alignVector.copy(_unitZ).applyQuaternion(quaternion).dot(this.eye)) < PLANE_HIDE_THRESHOLD)
//                            {
//                                handle.scale.set(1e-10, 1e-10, 1e-10);
//                                handle.visible = false;
//                            }
//                        }

//                        if (handle.name == "YZ")
//                        {
//                            if (JMath.abs(_alignVector.copy(_unitX).applyQuaternion(quaternion).dot(this.eye)) < PLANE_HIDE_THRESHOLD)
//                            {
//                                handle.scale.set(1e-10, 1e-10, 1e-10);
//                                handle.visible = false;
//                            }
//                        }

//                        if (handle.name == "XZ")
//                        {
//                            if (JMath.abs(_alignVector.copy(_unitY).applyQuaternion(quaternion).dot(this.eye)) < PLANE_HIDE_THRESHOLD)
//                            {
//                                handle.scale.set(1e-10, 1e-10, 1e-10);
//                                handle.visible = false;
//                            }
//                        }
//                    }
//                    else if (this.mode == "rotate")
//                    {
//                        // Align handles to current local or world rotation

//                        _tempQuaternion2.copy(quaternion);
//                        _alignVector.copy(this.eye).applyQuaternion(_tempQuaternion.copy(quaternion).invert());

//                        if (handle.name.search("E") != -1)
//                        {
//                            handle.quaternion.setFromRotationMatrix(_lookAtMatrix.lookAt(this.eye, _zeroVector, _unitY));
//                        }

//                        if (handle.name == "X")
//                        {
//                            _tempQuaternion.setFromAxisAngle(_unitX, JMath.atan2(-_alignVector.y, _alignVector.z));
//                            _tempQuaternion.multiplyQuaternions(_tempQuaternion2, _tempQuaternion);
//                            handle.quaternion.copy(_tempQuaternion);
//                        }

//                        if (handle.name == "Y")
//                        {
//                            _tempQuaternion.setFromAxisAngle(_unitY, JMath.atan2(_alignVector.x, _alignVector.z));
//                            _tempQuaternion.multiplyQuaternions(_tempQuaternion2, _tempQuaternion);
//                            handle.quaternion.copy(_tempQuaternion);
//                        }

//                        if (handle.name == "Z")
//                        {
//                            _tempQuaternion.setFromAxisAngle(_unitZ, JMath.atan2(_alignVector.y, _alignVector.x));
//                            _tempQuaternion.multiplyQuaternions(_tempQuaternion2, _tempQuaternion);
//                            handle.quaternion.copy(_tempQuaternion);
//                        }
//                    }

//                    // Hide disabled axes
//                    handle.visible = handle.visible && (handle.name.indexOf("X") == -1 || this.showX);
//                    handle.visible = handle.visible && (handle.name.indexOf("Y") == -1 || this.showY);
//                    handle.visible = handle.visible && (handle.name.indexOf("Z") == -1 || this.showZ);
//                    handle.visible = handle.visible && (handle.name.indexOf("E") == -1 || (this.showX && this.showY && this.showZ));

//                    // highlight selected axis
//                    var matObj = (handle as IMaterialObject).getMaterial();
//                    matObj.userData["_color"] = matObj.userData["_color"] ?? matObj.color.clone();
//                    matObj.userData["_opacity"] = matObj.userData["_opacity"] ?? matObj.opacity;

//                    matObj.color.copy((Color)matObj.userData["_color"]);
//                    matObj.opacity = (double)matObj.userData["_opacity"];

//                    if (this.enabled && this.axis != null)
//                    {
//                        if (handle.name == this.axis)
//                        {
//                            matObj.color.setHex(0xffff00);
//                            matObj.opacity = 1.0;
//                        }
//                        else if (this.axis.split("").some((a) =>
//                        {
//                            return handle.name == a;
//                        }))
//                        {
//                            matObj.color.setHex(0xffff00);
//                            matObj.opacity = 1.0;
//                        }
//                    }
//                }

//                base.updateMatrixWorld(force);
//            }
//        }

//        public class TransformControlsPlane : Mesh
//        {
//            public string space;
//            public string mode;
//            public string axis;
//            public Vector3 worldPosition;
//            public Vector3 eye;
//            public Quaternion worldQuaternion;
//            public Quaternion cameraQuaternion;

//            //public Camera camera;
//            //public Object3D obj;
//            //public bool enabled;
//            //public object translationSnap;
//            //public object rotationSnap;
//            //public object scaleSnap;
//            //public double size;
//            //public bool dragging;
//            //public bool showX;
//            //public bool showY;
//            //public bool showZ;

//            public TransformControlsPlane()
//                : base(
//                    new PlaneGeometry(100000, 100000, 2, 2),
//                    new MeshBasicMaterial
//                    {
//                        visible = false,
//                        wireframe = true,
//                        side = Constants.DoubleSide,
//                        transparent = true,
//                        opacity = 0.1,
//                        toneMapped = false
//                    })
//                )
//            {
//                this.type = "TransformControlsPlane";
//            }
//            public override void updateMatrixWorld(bool force = false)
//            {
//                var space = this.space;
//                this.position.copy(this.worldPosition);
//                if (this.mode == "scale")
//                    space = "local"; // scale always oriented to local rotation

//                _v1.copy(_unitX).applyQuaternion(space == "local" ? this.worldQuaternion : _identityQuaternion);
//                _v2.copy(_unitY).applyQuaternion(space == "local" ? this.worldQuaternion : _identityQuaternion);
//                _v3.copy(_unitZ).applyQuaternion(space == "local" ? this.worldQuaternion : _identityQuaternion);

//                // Align the plane for current transform mode, axis and space.

//                _alignVector.copy(_v2);

//                switch (this.mode)
//                {
//                    case "translate":
//                    case "scale":
//                        switch (this.axis)
//                        {
//                            case "X":
//                                _alignVector.copy(this.eye).cross(_v1);
//                                _dirVector.copy(_v1).cross(_alignVector);
//                                break;
//                            case "Y":
//                                _alignVector.copy(this.eye).cross(_v2);
//                                _dirVector.copy(_v2).cross(_alignVector);
//                                break;
//                            case "Z":
//                                _alignVector.copy(this.eye).cross(_v3);
//                                _dirVector.copy(_v3).cross(_alignVector);
//                                break;
//                            case "XY":
//                                _dirVector.copy(_v3);
//                                break;
//                            case "YZ":
//                                _dirVector.copy(_v1);
//                                break;
//                            case "XZ":
//                                _alignVector.copy(_v3);
//                                _dirVector.copy(_v2);
//                                break;
//                            case "XYZ":
//                            case "E":
//                                _dirVector.set(0, 0, 0);
//                                break;
//                        }
//                        break;
//                    case "rotate":
//                    default:
//                        // special case for rotate
//                        _dirVector.set(0, 0, 0);
//                        break;
//                }

//                if (_dirVector.length() == 0)
//                {
//                    // If in rotate mode, make the plane parallel to camera
//                    this.quaternion.copy(this.cameraQuaternion);
//                }
//                else
//                {
//                    _tempMatrix.lookAt(_tempVector.set(0, 0, 0), _dirVector, _alignVector);
//                    this.quaternion.setFromRotationMatrix(_tempMatrix);
//                }
//                base.updateMatrixWorld(force);

//            }
//        }

//        private static Raycaster _raycaster = new Raycaster();
//        private static Vector3 _tempVector = new Vector3();
//        private static Vector3 _tempVector2 = new Vector3();
//        private static Quaternion _tempQuaternion = new Quaternion();
//        private static Unit _unit = new Unit()
//        {
//            X = new Vector3(1, 0, 0),
//            Y = new Vector3(0, 1, 0),
//            Z = new Vector3(0, 0, 1)
//        };

//        private static EventArgs _changeEvent = new EventArgs { type = "change" };
//        private static EventArgs _mouseDownEvent = new EventArgs { type = "mouseDown" };
//        private static EventArgs _mouseUpEvent = new EventArgs { type = "mouseUp", mode = null };
//        private static EventArgs _objectChangeEvent = new EventArgs { type = "objectChange" };

//        public GLControl glControl;
//        public TransformControlsGizmo _gizmo;
//        public TransformControlsPlane _plane;

//        public Vector3 _offset;
//        public Vector3 _startNorm;
//        public Vector3 _endNorm;
//        public Vector3 _cameraScale;

//        public Vector3 _parentPosition;
//        public Quaternion _parentQuaternion;
//        public Quaternion _parentQuaternionInv;
//        public Vector3 _parentScale;

//        public Vector3 _worldScaleStart;
//        public Quaternion _worldQuaternionInv;
//        public Vector3 _worldScale;

//        public Vector3 _positionStart;
//        public Quaternion _quaternionStart;
//        public Vector3 _scaleStart;

//        #region Props
//        private Camera _cameraDefault;
//        private Camera _camera;
//        public Camera camera
//        {
//            get => _camera ?? _cameraDefault;
//            set
//            {
//                if (value != _camera)
//                {
//                    _camera = value;
//                    //this._plane.camera = value;
//                    this._gizmo.camera = value;
//                    this.dispatchEvent(new EventArgs { type = nameof(camera) + "-changed", value = value });
//                    this.dispatchEvent(_changeEvent);
//                }
//            }
//        }

//        private Object3D _objDefault;
//        private Object3D _obj;
//        public Object3D obj
//        {
//            get => _obj ?? _objDefault;
//            set
//            {
//                if (value != _obj)
//                {
//                    _obj = value;
//                    //this._plane.obj = value;
//                    //this._gizmo.obj = value;
//                    this.dispatchEvent(new EventArgs { type = nameof(obj) + "-changed", value = value });
//                    this.dispatchEvent(_changeEvent);
//                }
//            }
//        }

//        private bool _enabledDefault;
//        private bool? _enabled;
//        public bool enabled
//        {
//            get => _enabled ?? _enabledDefault;
//            set
//            {
//                if (value != _enabled)
//                {
//                    _enabled = value;
//                    //this._plane.enabled = value;
//                    this._gizmo.enabled = value;

//                    this.dispatchEvent(new EventArgs { type = nameof(enabled) + "-changed", value = value });
//                    this.dispatchEvent(_changeEvent);
//                }
//            }
//        }

//        private string _axisDefault;
//        private string _axis;
//        public string axis
//        {
//            get => _axis ?? _axisDefault;
//            set
//            {
//                if (value != _axis)
//                {
//                    _axis = value;
//                    this._plane.axis = value;
//                    this._gizmo.axis = value;
//                    this.dispatchEvent(new EventArgs { type = nameof(axis) + "-changed", value = value });
//                    this.dispatchEvent(_changeEvent);
//                }
//            }
//        }

//        private string _modeDefault;
//        private string _mode;
//        public string mode
//        {
//            get => _mode ?? _modeDefault;
//            set
//            {
//                if (value != _mode)
//                {
//                    _mode = value;
//                    this._plane.mode = value;
//                    this._gizmo.mode = value;
//                    this.dispatchEvent(new EventArgs { type = nameof(mode) + "-changed", value = value });
//                    this.dispatchEvent(_changeEvent);
//                }
//            }
//        }

//        private double? _translationSnapDefault;
//        private double? _translationSnap;
//        public double? translationSnap
//        {
//            get => _translationSnap ?? _translationSnapDefault;
//            set
//            {
//                if (value != _translationSnap)
//                {
//                    _translationSnap = value;
//                    //this._plane.translationSnap = value;
//                    //this._gizmo.translationSnap = value;
//                    this.dispatchEvent(new EventArgs { type = nameof(translationSnap) + "-changed", value = value });
//                    this.dispatchEvent(_changeEvent);
//                }
//            }
//        }

//        private double? _rotationSnapDefault;
//        private double? _rotationSnap;
//        public double? rotationSnap
//        {
//            get => _rotationSnap ?? _rotationSnapDefault;
//            set
//            {
//                if (value != _rotationSnap)
//                {
//                    _rotationSnap = value;
//                    //this._plane.rotationSnap = value;
//                    //this._gizmo.rotationSnap = value;
//                    this.dispatchEvent(new EventArgs { type = nameof(rotationSnap) + "-changed", value = value });
//                    this.dispatchEvent(_changeEvent);
//                }
//            }
//        }

//        private double? _scaleSnapDefault;
//        private double? _scaleSnap;
//        public double? scaleSnap
//        {
//            get => _scaleSnap ?? _scaleSnapDefault;
//            set
//            {
//                if (value != _scaleSnap)
//                {
//                    _scaleSnap = value;
//                    //this._plane.scaleSnap = value;
//                    //this._gizmo.scaleSnap = value;
//                    this.dispatchEvent(new EventArgs { type = nameof(scaleSnap) + "-changed", value = value });
//                    this.dispatchEvent(_changeEvent);
//                }
//            }
//        }

//        private string _spaceDefault;
//        private string _space;
//        public string space
//        {
//            get => _space ?? _spaceDefault;
//            set
//            {
//                if (value != _space)
//                {
//                    _space = value;
//                    this._plane.space = value;
//                    this._gizmo.space = value;
//                    this.dispatchEvent(new EventArgs { type = nameof(space) + "-changed", value = value });
//                    this.dispatchEvent(_changeEvent);
//                }
//            }
//        }

//        private double _sizeDefault;
//        private double? _size;
//        public double size
//        {
//            get => _size ?? _sizeDefault;
//            set
//            {
//                if (value != _size)
//                {
//                    _size = value;
//                    //this._plane.size = value;
//                    this._gizmo.size = value;
//                    this.dispatchEvent(new EventArgs { type = nameof(size) + "-changed", value = value });
//                    this.dispatchEvent(_changeEvent);
//                }
//            }
//        }

//        private bool _draggingDefault;
//        private bool? _dragging;
//        public bool dragging
//        {
//            get => _dragging ?? _draggingDefault;
//            set
//            {
//                if (value != _dragging)
//                {
//                    _dragging = value;
//                    //this._plane.dragging = value;
//                    this._gizmo.dragging = value;
//                    this.dispatchEvent(new EventArgs { type = nameof(dragging) + "-changed", value = value });
//                    this.dispatchEvent(_changeEvent);
//                }
//            }
//        }

//        private bool _showXDefault;
//        private bool? _showX;
//        public bool showX
//        {
//            get => _showX ?? _showXDefault;
//            set
//            {
//                if (value != _showX)
//                {
//                    _showX = value;
//                    //this._plane.showX = value;
//                    this._gizmo.showX = value;
//                    this.dispatchEvent(new EventArgs { type = nameof(showX) + "-changed", value = value });
//                    this.dispatchEvent(_changeEvent);
//                }
//            }
//        }

//        private bool _showYDefault;
//        private bool? _showY;
//        public bool showY
//        {
//            get => _showY ?? _showYDefault;
//            set
//            {
//                if (value != _showY)
//                {
//                    _showY = value;
//                    //this._plane.showY = value;
//                    this._gizmo.showY = value;
//                    this.dispatchEvent(new EventArgs { type = nameof(showY) + "-changed", value = value });
//                    this.dispatchEvent(_changeEvent);
//                }
//            }
//        }

//        private bool _showZDefault;
//        private bool? _showZ;
//        public bool showZ
//        {
//            get => _showZ ?? _showZDefault;
//            set
//            {
//                if (value != _showZ)
//                {
//                    _showZ = value;
//                    //this._plane.showZ = value;
//                    this._gizmo.showZ = value;
//                    this.dispatchEvent(new EventArgs { type = nameof(showZ) + "-changed", value = value });
//                    this.dispatchEvent(_changeEvent);
//                }
//            }
//        }

//        private Vector3 _worldPositionDefault;
//        private Vector3 _worldPosition;
//        public Vector3 worldPosition
//        {
//            get => _worldPosition ?? _worldPositionDefault;
//            set
//            {
//                if (value != _worldPosition)
//                {
//                    _worldPosition = value;
//                    this._plane.worldPosition = value;
//                    this._gizmo.worldPosition = value;
//                    this.dispatchEvent(new EventArgs { type = nameof(worldPosition) + "-changed", value = value });
//                    this.dispatchEvent(_changeEvent);
//                }
//            }
//        }

//        private Vector3 _worldPositionStartDefault;
//        private Vector3 _worldPositionStart;
//        public Vector3 worldPositionStart
//        {
//            get => _worldPositionStart ?? _worldPositionStartDefault;
//            set
//            {
//                if (value != _worldPositionStart)
//                {
//                    _worldPositionStart = value;
//                    //this._plane.worldPositionStart = value;
//                    this._gizmo.worldPositionStart = value;
//                    this.dispatchEvent(new EventArgs { type = nameof(worldPositionStart) + "-changed", value = value });
//                    this.dispatchEvent(_changeEvent);
//                }
//            }
//        }

//        private Quaternion _worldQuaternionDefault;
//        private Quaternion _worldQuaternion;
//        public Quaternion worldQuaternion
//        {
//            get => _worldQuaternion ?? _worldQuaternionDefault;
//            set
//            {
//                if (value != _worldQuaternion)
//                {
//                    _worldQuaternion = value;
//                    this._plane.worldQuaternion = value;
//                    this._gizmo.worldQuaternion = value;
//                    this.dispatchEvent(new EventArgs { type = nameof(worldQuaternion) + "-changed", value = value });
//                    this.dispatchEvent(_changeEvent);
//                }
//            }
//        }

//        private Quaternion _worldQuaternionStartDefault;
//        private Quaternion _worldQuaternionStart;
//        public Quaternion worldQuaternionStart
//        {
//            get => _worldQuaternionStart ?? _worldQuaternionStartDefault;
//            set
//            {
//                if (value != _worldQuaternionStart)
//                {
//                    _worldQuaternionStart = value;
//                    //this._plane.worldQuaternionStart = value;
//                    this._gizmo.worldQuaternionStart = value;
//                    this.dispatchEvent(new EventArgs { type = nameof(worldQuaternionStart) + "-changed", value = value });
//                    this.dispatchEvent(_changeEvent);
//                }
//            }
//        }

//        private Vector3 _cameraPositionDefault;
//        private Vector3 _cameraPosition;
//        public Vector3 cameraPosition
//        {
//            get => _cameraPosition ?? _cameraPositionDefault;
//            set
//            {
//                if (value != _cameraPosition)
//                {
//                    _cameraPosition = value;
//                    //this._plane.cameraPosition = value;
//                    this._gizmo.cameraPosition = value;
//                    this.dispatchEvent(new EventArgs { type = nameof(cameraPosition) + "-changed", value = value });
//                    this.dispatchEvent(_changeEvent);
//                }
//            }
//        }

//        private Quaternion _cameraQuaternionDefault;
//        private Quaternion _cameraQuaternion;
//        public Quaternion cameraQuaternion
//        {
//            get => _cameraQuaternion ?? _cameraQuaternionDefault;
//            set
//            {
//                if (value != _cameraQuaternion)
//                {
//                    _cameraQuaternion = value;
//                    this._plane.cameraQuaternion = value;
//                    //this._gizmo.cameraQuaternion = value;
//                    this.dispatchEvent(new EventArgs { type = nameof(cameraQuaternion) + "-changed", value = value });
//                    this.dispatchEvent(_changeEvent);
//                }
//            }
//        }

//        private Vector3 _pointStartDefault;
//        private Vector3 _pointStart;
//        public Vector3 pointStart
//        {
//            get => _pointStart ?? _pointStartDefault;
//            set
//            {
//                if (value != _pointStart)
//                {
//                    _pointStart = value;
//                    //this._plane.pointStart = value;
//                    //this._gizmo.pointStart = value;
//                    this.dispatchEvent(new EventArgs { type = nameof(pointStart) + "-changed", value = value });
//                    this.dispatchEvent(_changeEvent);
//                }
//            }
//        }

//        private Vector3 _pointEndDefault;
//        private Vector3 _pointEnd;
//        public Vector3 pointEnd
//        {
//            get => _pointEnd ?? _pointEndDefault;
//            set
//            {
//                if (value != _pointEnd)
//                {
//                    _pointEnd = value;
//                    //this._plane.pointEnd = value;
//                    //this._gizmo.pointEnd = value;
//                    this.dispatchEvent(new EventArgs { type = nameof(pointEnd) + "-changed", value = value });
//                    this.dispatchEvent(_changeEvent);
//                }
//            }
//        }

//        private Vector3 _rotationAxisDefault;
//        private Vector3 _rotationAxis;
//        public Vector3 rotationAxis
//        {
//            get => _rotationAxis ?? _rotationAxisDefault;
//            set
//            {
//                if (value != _rotationAxis)
//                {
//                    _rotationAxis = value;
//                    //this._plane.rotationAxis = value;
//                    this._gizmo.rotationAxis = value;
//                    this.dispatchEvent(new EventArgs { type = nameof(rotationAxis) + "-changed", value = value });
//                    this.dispatchEvent(_changeEvent);
//                }
//            }
//        }

//        private double _rotationAngleDefault;
//        private double? _rotationAngle;
//        public double rotationAngle
//        {
//            get => _rotationAngle ?? _rotationAngleDefault;
//            set
//            {
//                if (value != _rotationAngle)
//                {
//                    _rotationAngle = value;
//                    //this._plane.rotationAngle = value;
//                    //this._gizmo.rotationAngle = value;
//                    this.dispatchEvent(new EventArgs { type = nameof(rotationAngle) + "-changed", value = value });
//                    this.dispatchEvent(_changeEvent);
//                }
//            }
//        }

//        private Vector3 _eyeDefault;
//        private Vector3 _eye;
//        public Vector3 eye
//        {
//            get => _eye ?? _eyeDefault;
//            set
//            {
//                if (value != _eye)
//                {
//                    _eye = value;
//                    this._plane.eye = value;
//                    this._gizmo.eye = value;
//                    this.dispatchEvent(new EventArgs { type = nameof(eye) + "-changed", value = value });
//                    this.dispatchEvent(_changeEvent);
//                }
//            }
//        }
//        #endregion Props
//        public TransformControls(Camera camera, GLControl domElement)
//        {
//            if (domElement == null)
//            {
//                console.warn("THREE.TransformControls: The second parameter \"domElement\" is now mandatory.");
//                //domElement = document;
//            }

//            this.visible = false;
//            this.glControl = domElement;
//            //this.domElement.style.touchAction = 'none'; // disable touch scroll

//            var _gizmo = new TransformControlsGizmo();
//            this._gizmo = _gizmo;
//            this.add(_gizmo);

//            var _plane = new TransformControlsPlane();
//            this._plane = _plane;
//            this.add(_plane);

//            // Define properties with getters/setter
//            // Setting the defined property will automatically trigger change event
//            // Defined properties are passed down to gizmo and plane

//            defineProperty("camera", camera);
//            defineProperty("object", null);
//            defineProperty("enabled", true);
//            defineProperty("axis", null);
//            defineProperty("mode", "translate");
//            defineProperty("translationSnap", null);
//            defineProperty("rotationSnap", null);
//            defineProperty("scaleSnap", null);
//            defineProperty("space", "world");
//            defineProperty("size", 1);
//            defineProperty("dragging", false);
//            defineProperty("showX", true);
//            defineProperty("showY", true);
//            defineProperty("showZ", true);

//            // Reusable utility variables

//            var worldPosition = new Vector3();
//            var worldPositionStart = new Vector3();
//            var worldQuaternion = new Quaternion();
//            var worldQuaternionStart = new Quaternion();
//            var cameraPosition = new Vector3();
//            var cameraQuaternion = new Quaternion();
//            var pointStart = new Vector3();
//            var pointEnd = new Vector3();
//            var rotationAxis = new Vector3();
//            var rotationAngle = 0;
//            var eye = new Vector3();

//            // TODO: remove properties unused in plane and gizmo

//            defineProperty("worldPosition", worldPosition);
//            defineProperty("worldPositionStart", worldPositionStart);
//            defineProperty("worldQuaternion", worldQuaternion);
//            defineProperty("worldQuaternionStart", worldQuaternionStart);
//            defineProperty("cameraPosition", cameraPosition);
//            defineProperty("cameraQuaternion", cameraQuaternion);
//            defineProperty("pointStart", pointStart);
//            defineProperty("pointEnd", pointEnd);
//            defineProperty("rotationAxis", rotationAxis);
//            defineProperty("rotationAngle", rotationAngle);
//            defineProperty("eye", eye);

//            this._offset = new Vector3();
//            this._startNorm = new Vector3();
//            this._endNorm = new Vector3();
//            this._cameraScale = new Vector3();

//            this._parentPosition = new Vector3();
//            this._parentQuaternion = new Quaternion();
//            this._parentQuaternionInv = new Quaternion();
//            this._parentScale = new Vector3();

//            this._worldScaleStart = new Vector3();
//            this._worldQuaternionInv = new Quaternion();
//            this._worldScale = new Vector3();

//            this._positionStart = new Vector3();
//            this._quaternionStart = new Quaternion();
//            this._scaleStart = new Vector3();

//            //this._getPointer = getPointer.bind(this);
//            //this._onPointerDown = onPointerDown.bind(this);
//            //this._onPointerHover = onPointerHover.bind(this);
//            //this._onPointerMove = onPointerMove.bind(this);
//            //this._onPointerUp = onPointerUp.bind(this);

//            this.glControl.MouseDown += this.onPointerDown;
//            this.glControl.MouseMove += this.onPointerHover;
//            this.glControl.MouseUp += this.onPointerUp;
//        }

//        /// <summary>
//        /// Defined getter, setter and store for a property
//        /// </summary>
//        public void defineProperty(string propName, object defaultValue)
//        {
//            var propValue = defaultValue;
//            switch (propName)
//            {
//                case "camera":
//                    this._cameraDefault = propValue as Camera;
//                    this.camera = this._cameraDefault;
//                    break;
//                case "object":
//                    this._objDefault = propValue as Object3D;
//                    this.obj = _objDefault;
//                    break;
//                case "enabled":
//                    this._enabledDefault = Convert.ToBoolean(propValue);
//                    this.enabled = _enabledDefault;
//                    break;
//                case "axis":
//                    this._axisDefault = (string)propValue;
//                    this.axis = _axisDefault;
//                    break;
//                case "mode":
//                    this._modeDefault = (string)propValue;
//                    this.mode = _modeDefault;
//                    break;
//                case "translationSnap":
//                    if (propValue != null)
//                    {
//                        this._translationSnapDefault = Convert.ToDouble(propValue);
//                        this.translationSnap = _translationSnapDefault;
//                    }
//                    break;
//                case "rotationSnap":
//                    if (propValue != null)
//                    {
//                        this._rotationSnapDefault = Convert.ToDouble(propValue);
//                        this.rotationSnap = _rotationSnapDefault;
//                    }
//                    break;
//                case "scaleSnap":
//                    if (propValue != null)
//                    {
//                        this._scaleSnapDefault = Convert.ToDouble(propValue);
//                        this.scaleSnap = _scaleSnapDefault;
//                    }
//                    break;
//                case "space":
//                    this._spaceDefault = (string)propValue;
//                    this.space = _spaceDefault;
//                    break;
//                case "size":
//                    this._sizeDefault = Convert.ToDouble(propValue);
//                    this.size = _sizeDefault;
//                    break;
//                case "dragging":
//                    this._draggingDefault = Convert.ToBoolean(propValue);
//                    this.dragging = _draggingDefault;
//                    break;
//                case "showX":
//                    this._showXDefault = Convert.ToBoolean(propValue);
//                    this.showX = _showXDefault;
//                    break;
//                case "showY":
//                    this._showYDefault = Convert.ToBoolean(propValue);
//                    this.showY = _showXDefault;
//                    break;
//                case "showZ":
//                    this._showZDefault = Convert.ToBoolean(propValue);
//                    this.showZ = _showZDefault;
//                    break;
//                case "worldPosition":
//                    this._worldPositionDefault = propValue as Vector3;
//                    this.worldPosition = _worldPositionDefault;
//                    break;
//                case "worldPositionStart":
//                    this._worldPositionStartDefault = propValue as Vector3;
//                    this.worldPositionStart = _worldPositionStartDefault;
//                    break;
//                case "worldQuaternion":
//                    this._worldQuaternionDefault = propValue as Quaternion;
//                    this.worldQuaternion = _worldQuaternionDefault;
//                    break;
//                case "worldQuaternionStart":
//                    this._worldQuaternionStartDefault = propValue as Quaternion;
//                    this.worldQuaternionStart = _worldQuaternionStartDefault;
//                    break;
//                case "cameraPosition":
//                    this._cameraPositionDefault = propValue as Vector3;
//                    this.cameraPosition = _cameraPositionDefault;
//                    break;
//                case "cameraQuaternion":
//                    this._cameraQuaternionDefault = propValue as Quaternion;
//                    this.cameraQuaternion = _cameraQuaternionDefault;
//                    break;
//                case "pointStart":
//                    this._pointStartDefault = propValue as Vector3;
//                    this.pointStart = _pointStartDefault;
//                    break;
//                case "pointEnd":
//                    this._pointEndDefault = propValue as Vector3;
//                    this.pointEnd = _pointEndDefault;
//                    break;
//                case "rotationAxis":
//                    this._rotationAxisDefault = propValue as Vector3;
//                    this.rotationAxis = _rotationAxisDefault;
//                    break;
//                case "rotationAngle":
//                    this._rotationAngleDefault = Convert.ToDouble(propValue);
//                    this.rotationAngle = _rotationAngleDefault;
//                    break;
//                case "eye":
//                    this._eyeDefault = propValue as Vector3;
//                    this.eye = _eyeDefault;
//                    break;
//                default:
//                    break;
//            }
//        }

//        public override void updateMatrixWorld(bool force = false)
//        {
//            if (this.obj != null)
//            {
//                this.obj.updateMatrixWorld();

//                if (this.obj.parent == null)
//                {
//                    console.error("TransformControls: The attached 3D object must be a part of the scene graph.");
//                }
//                else
//                {
//                    this.obj.parent.matrixWorld.decompose(this._parentPosition, this._parentQuaternion, this._parentScale);
//                }

//                this.obj.matrixWorld.decompose(this.worldPosition, this.worldQuaternion, this._worldScale);

//                this._parentQuaternionInv.copy(this._parentQuaternion).invert();
//                this._worldQuaternionInv.copy(this.worldQuaternion).invert();
//            }

//            this.camera.updateMatrixWorld();
//            this.camera.matrixWorld.decompose(this.cameraPosition, this.cameraQuaternion, this._cameraScale);

//            if (this.camera is OrthographicCamera)
//            {
//                this.camera.getWorldDirection(this.eye).negate();
//            }
//            else
//            {
//                this.eye.copy(this.cameraPosition).sub(this.worldPosition).normalize();
//            }
//            base.updateMatrixWorld(force);
//        }

//        private void pointerHover(Pointer pointer)
//        {
//            if (this.obj == null || this.dragging)
//                return;

//            _raycaster.setFromCamera(pointer.ToV2(), this.camera);

//            if (intersectObjectWithRay(this._gizmo.picker[this.mode], _raycaster, out Raycaster.Intersection intersect))
//            {
//                this.axis = intersect.target.name;
//            }
//            else
//            {
//                this.axis = null;
//            }
//        }

//        private void pointerDown(Pointer pointer)
//        {
//            if (this.obj == null || this.dragging == true || pointer.button != MouseButtons.Left)
//                return;

//            if (this.axis != null)
//            {
//                _raycaster.setFromCamera(pointer.ToV2(), this.camera);

//                if (intersectObjectWithRay(this._plane, _raycaster, out Raycaster.Intersection planeIntersect, true))
//                {
//                    this.obj.updateMatrixWorld();
//                    this.obj.parent.updateMatrixWorld();

//                    this._positionStart.copy(this.obj.position);
//                    this._quaternionStart.copy(this.obj.quaternion);
//                    this._scaleStart.copy(this.obj.scale);

//                    this.obj.matrixWorld.decompose(this.worldPositionStart, this.worldQuaternionStart, this._worldScaleStart);

//                    this.pointStart.copy(planeIntersect.point).sub(this.worldPositionStart);
//                }

//                this.dragging = true;
//                _mouseDownEvent.mode = this.mode;
//                this.dispatchEvent(_mouseDownEvent);
//            }
//        }

//        private void pointerMove(Pointer pointer)
//        {
//            var axis = this.axis;
//            var mode = this.mode;
//            var obj = this.obj;
//            var space = this.space;

//            if (mode == "scale")
//            {
//                space = "local";
//            }
//            else if (axis == "E" || axis == "XYZE" || axis == "XYZ")
//            {
//                space = "world";
//            }

//            if (obj == null || axis == null || this.dragging == false)// || pointer.button != -1)
//                return;

//            _raycaster.setFromCamera(pointer.ToV2(), this.camera);

//            if (!intersectObjectWithRay(this._plane, _raycaster, out Raycaster.Intersection planeIntersect, true))
//                return;

//            this.pointEnd.copy(planeIntersect.point).sub(this.worldPositionStart);

//            if (mode == "translate")
//            {
//                // Apply translate

//                this._offset.copy(this.pointEnd).sub(this.pointStart);

//                if (space == "local" && axis != "XYZ")
//                {
//                    this._offset.applyQuaternion(this._worldQuaternionInv);
//                }

//                if (axis.indexOf("X") == -1) this._offset.x = 0;
//                if (axis.indexOf("Y") == -1) this._offset.y = 0;
//                if (axis.indexOf("Z") == -1) this._offset.z = 0;

//                if (space == "local" && axis != "XYZ")
//                {
//                    this._offset.applyQuaternion(this._quaternionStart).divide(this._parentScale);
//                }
//                else
//                {
//                    this._offset.applyQuaternion(this._parentQuaternionInv).divide(this._parentScale);
//                }

//                obj.position.copy(this._offset).add(this._positionStart);

//                // Apply translation snap

//                if (this.translationSnap != null)
//                {
//                    var translationSnap = (double)this.translationSnap;
//                    if (space == "local")
//                    {
//                        obj.position.applyQuaternion(_tempQuaternion.copy(this._quaternionStart).invert());

//                        if (axis.search("X") != -1)
//                        {
//                            obj.position.x = JMath.round(obj.position.x / translationSnap) * translationSnap;
//                        }

//                        if (axis.search("Y") != -1)
//                        {
//                            obj.position.y = JMath.round(obj.position.y / translationSnap) * translationSnap;
//                        }

//                        if (axis.search("Z") != -1)
//                        {
//                            obj.position.z = JMath.round(obj.position.z / translationSnap) * translationSnap;
//                        }

//                        obj.position.applyQuaternion(this._quaternionStart);

//                    }

//                    if (space == "world")
//                    {
//                        if (obj.parent != null)
//                        {
//                            obj.position.add(_tempVector.setFromMatrixPosition(obj.parent.matrixWorld));
//                        }

//                        if (axis.search("X") != -1)
//                        {
//                            obj.position.x = JMath.round(obj.position.x / translationSnap) * translationSnap;
//                        }

//                        if (axis.search("Y") != -1)
//                        {
//                            obj.position.y = JMath.round(obj.position.y / translationSnap) * translationSnap;
//                        }

//                        if (axis.search("Z") != -1)
//                        {
//                            obj.position.z = JMath.round(obj.position.z / translationSnap) * translationSnap;
//                        }

//                        if (obj.parent != null)
//                        {
//                            obj.position.sub(_tempVector.setFromMatrixPosition(obj.parent.matrixWorld));
//                        }
//                    }
//                }
//            }
//            else if (mode == "scale")
//            {

//                if (axis.search("XYZ") != -1)
//                {
//                    var d = this.pointEnd.length() / this.pointStart.length();

//                    if (this.pointEnd.dot(this.pointStart) < 0) d *= -1;

//                    _tempVector2.set(d, d, d);
//                }
//                else
//                {

//                    _tempVector.copy(this.pointStart);
//                    _tempVector2.copy(this.pointEnd);

//                    _tempVector.applyQuaternion(this._worldQuaternionInv);
//                    _tempVector2.applyQuaternion(this._worldQuaternionInv);

//                    _tempVector2.divide(_tempVector);

//                    if (axis.search("X") == -1)
//                    {
//                        _tempVector2.x = 1;
//                    }

//                    if (axis.search("Y") == -1)
//                    {
//                        _tempVector2.y = 1;
//                    }

//                    if (axis.search("Z") == -1)
//                    {
//                        _tempVector2.z = 1;
//                    }
//                }

//                // Apply scale

//                obj.scale.copy(this._scaleStart).multiply(_tempVector2);

//                if (this.scaleSnap != null)
//                {
//                    var scaleSnap = (double)this.scaleSnap;
//                    if (axis.search("X") != -1)
//                    {
//                        obj.scale.x = JMath.round(obj.scale.x / scaleSnap) * scaleSnap;// || scaleSnap;
//                    }

//                    if (axis.search("Y") != -1)
//                    {
//                        obj.scale.y = JMath.round(obj.scale.y / scaleSnap) * scaleSnap;// || scaleSnap;
//                    }

//                    if (axis.search("Z") != -1)
//                    {
//                        obj.scale.z = JMath.round(obj.scale.z / scaleSnap) * scaleSnap;// || scaleSnap;
//                    }
//                }
//            }
//            else if (mode == "rotate")
//            {
//                this._offset.copy(this.pointEnd).sub(this.pointStart);

//                var ROTATION_SPEED = 20 / this.worldPosition.distanceTo(_tempVector.setFromMatrixPosition(this.camera.matrixWorld));

//                if (axis == "E")
//                {
//                    this.rotationAxis.copy(this.eye);
//                    this.rotationAngle = this.pointEnd.angleTo(this.pointStart);

//                    this._startNorm.copy(this.pointStart).normalize();
//                    this._endNorm.copy(this.pointEnd).normalize();

//                    this.rotationAngle *= (this._endNorm.cross(this._startNorm).dot(this.eye) < 0 ? 1 : -1);
//                }
//                else if (axis == "XYZE")
//                {
//                    this.rotationAxis.copy(this._offset).cross(this.eye).normalize();
//                    this.rotationAngle = this._offset.dot(_tempVector.copy(this.rotationAxis).cross(this.eye)) * ROTATION_SPEED;
//                }
//                else if (axis == "X" || axis == "Y" || axis == "Z")
//                {
//                    this.rotationAxis.copy(_unit[axis]);

//                    _tempVector.copy(_unit[axis]);

//                    if (space == "local")
//                    {

//                        _tempVector.applyQuaternion(this.worldQuaternion);

//                    }

//                    this.rotationAngle = this._offset.dot(_tempVector.cross(this.eye).normalize()) * ROTATION_SPEED;

//                }

//                // Apply rotation snap

//                if (this.rotationSnap != null)
//                {
//                    var rotationSnap = (double)this.rotationSnap;
//                    this.rotationAngle = JMath.round(this.rotationAngle / rotationSnap) * rotationSnap;
//                }

//                // Apply rotate
//                if (space == "local" && axis != "E" && axis != "XYZE")
//                {

//                    obj.quaternion.copy(this._quaternionStart);
//                    obj.quaternion.multiply(_tempQuaternion.setFromAxisAngle(this.rotationAxis, this.rotationAngle)).normalize();

//                }
//                else
//                {

//                    this.rotationAxis.applyQuaternion(this._parentQuaternionInv);
//                    obj.quaternion.copy(_tempQuaternion.setFromAxisAngle(this.rotationAxis, this.rotationAngle));
//                    obj.quaternion.multiply(this._quaternionStart).normalize();

//                }

//            }

//            this.dispatchEvent(_changeEvent);
//            this.dispatchEvent(_objectChangeEvent);

//        }

//        private void pointerUp(Pointer pointer)
//        {
//            if (pointer.button != MouseButtons.Left)
//                return;

//            if (this.dragging && (this.axis != null))
//            {
//                _mouseUpEvent.mode = this.mode;
//                this.dispatchEvent(_mouseUpEvent);
//            }

//            this.dragging = false;
//            this.axis = null;
//        }

//        public void dispose()
//        {
//            this.glControl.MouseDown -= this.onPointerDown;
//            this.glControl.MouseMove -= this.onPointerHover;
//            this.glControl.MouseMove -= this.onPointerMove;
//            this.glControl.MouseMove -= this.onPointerUp;

//            this.traverse((child) =>
//            {
//                if (child is ISolid)
//                {
//                    (child as ISolid).getGeometry().dispose();
//                    (child as ISolid).getMaterial().dispose();
//                }
//            });
//        }


//        // Set current object
//        public TransformControls attach(Object3D obj)
//        {
//            this.obj = obj;
//            this.visible = true;

//            return this;
//        }

//        // Detach from object
//        public TransformControls detach()
//        {
//            this.obj = null;
//            this.visible = false;
//            this.axis = null;

//            return this;
//        }

//        public void reset()
//        {
//            if (!this.enabled)
//                return;

//            if (this.dragging)
//            {
//                this.obj.position.copy(this._positionStart);
//                this.obj.quaternion.copy(this._quaternionStart);
//                this.obj.scale.copy(this._scaleStart);

//                this.dispatchEvent(_changeEvent);
//                this.dispatchEvent(_objectChangeEvent);

//                this.pointStart.copy(this.pointEnd);
//            }
//        }

//        public Raycaster getRaycaster()
//        {
//            return _raycaster;
//        }

//        // TODO: deprecate

//        public string getMode()
//        {
//            return this.mode;
//        }

//        public void setMode(string mode)
//        {
//            this.mode = mode;
//        }

//        public void setTranslationSnap(double translationSnap)
//        {
//            this.translationSnap = translationSnap;
//        }

//        public void setRotationSnap(double rotationSnap)
//        {
//            this.rotationSnap = rotationSnap;
//        }

//        public void setScaleSnap(double scaleSnap)
//        {
//            this.scaleSnap = scaleSnap;
//        }

//        public void setSize(double size)
//        {
//            this.size = size;
//        }

//        public void setSpace(string space)
//        {
//            this.space = space;
//        }


//        // mouse / touch event handlers
//        private Pointer getPointer(System.Windows.Forms.MouseEventArgs args)
//        {
//            var rect = this.glControl;

//            return new Pointer()
//            {
//                x = (args.X - rect.Left) / (double)rect.Width * 2 - 1,
//                y = -(args.Y - rect.Top) / (double)rect.Height * 2 + 1,
//                button = args.Button
//            };
//        }

//        private void onPointerHover(object sender, MouseEventArgs args)
//        {
//            if (!this.enabled) return;

//            this.pointerHover(this.getPointer(args));
//        }
//        private void onPointerDown(object sender, MouseEventArgs args)
//        {
//            if (!this.enabled) return;

//            this.glControl.MouseMove += this.onPointerMove;

//            this.pointerHover(this.getPointer(args));
//            this.pointerDown(this.getPointer(args));
//        }
//        private void onPointerMove(object sender, MouseEventArgs args)
//        {
//            if (!this.enabled) return;

//            this.pointerMove(this.getPointer(args));
//        }

//        private void onPointerUp(object sender, MouseEventArgs args)
//        {
//            if (!this.enabled) return;

//            this.glControl.MouseMove -= this.onPointerMove;

//            this.pointerUp(this.getPointer(args));
//        }

//        private bool intersectObjectWithRay(Object3D obj, Raycaster raycaster, out Raycaster.Intersection intersection, bool includeInvisible = false)
//        {
//            intersection = null;
//            var allIntersections = raycaster.intersectObject(obj, true);

//            for (int i = 0; i < allIntersections.length; i++)
//            {
//                if (allIntersections[i].target.visible || includeInvisible)
//                {
//                    intersection = allIntersections[i];
//                    return true;
//                }
//            }
//            return false;
//        }
//    }
//}